package com.idas;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.util.Enumeration;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.ws.Endpoint;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;

import com.idas.IDASAuthentication;
import com.idas.common.TouchFile;
import com.idas.db.ConnectionHandler;

import com.idas.service.API;

public class IDASMain {

    static Logger logger = Logger.getLogger(IDASMain.class.getName());

    public static void main(String args[]) {

        File touchFile;
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream(args[0]));
            Enumeration<Object> enu = prop.keys();
            while (enu.hasMoreElements()) {
                Object obj = enu.nextElement();
                System.setProperty(obj.toString(), prop.getProperty(obj.toString()));
            }

        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
            System.out.println( "No property file found "+args[0]);
            System.exit(1);
        } catch (IOException e1) {
            e1.printStackTrace();
            System.out.println("Error While reading the property file  "+args[0]);
            System.exit(1);
        }


        // System.out.println(System.getProperty("LOG_4_J"));
        PropertyConfigurator.configure(System.getProperty("LOG_4_J"));
        logger.info("Starting IDAS..");

        touchFile = new File(System.getProperty("TOUCH_FILE_DIR") + "idas");
        try {

            touchFile.createNewFile();
            logger.info("Touch File Creation success " + touchFile.getCanonicalPath() + "..");
        } catch (IOException e1) {
        	e1.printStackTrace();
            logger.error("Cound not create the touch file in" + System.getProperty("TOUCH_FILE_DIR") + " ");
            logger.error(e1.getMessage());
            System.exit(0);
            e1.printStackTrace();
        }

        try {
            logger.info("Initializing the DB..");
            ConnectionHandler.initDBhandler();
            logger.info("DB initialization Success..");

            // Setting up the API service
            if (System.getProperty("API_ACCESS") != null && System.getProperty("API_ACCESS").trim().equals("1")) {

                new Thread() {

                    public void run() {

                        try {

                            logger.info("Starting API Service..");

                            String host = System.getProperty("API_HOST","0.0.0.0");// getHostAddress();

                            // Getting the port
                            String port = System.getProperty("API_PORT");
                            if (port != null) {

                                int po, max_conns = 400, max_thread = 100;

                                String conns = System.getProperty("API_MAX_CONNS");
                                if (conns != null) {
                                    max_conns = Integer.parseInt(conns.trim());
                                    logger.info("Creating the API service to listen " + max_conns + " simultaneous connections");
                                } else {
                                    logger.info("API_MAX_CONNS value not set using the default value of " + max_conns);
                                }

                                String threads = System.getProperty("API_MAX_THREADS");
                                if (threads != null) {
                                    max_thread = Integer.parseInt(threads.trim());
                                    logger.info("Creating the API service to execute " + max_thread + " simultaneous executions");
                                } else {
                                    logger.info("API_MAX_THREADS value not set using the default value of " + max_thread);
                                }

                                po = Integer.parseInt(port.trim());

                                String api_path = "/API";

                                /*String endpoint = "https://" + host + ":" + po + path;
                                logger.info("API service published at " + endpoint);
                                logger.info("API service WSDL location at " + endpoint + "?wsdl");*/
                                HttpServer server = null;
                                
                                int https =  Integer.parseInt(System.getProperty("HTTPS", "0"));
                                if(https==1){
                                	logger.info("Starting https Service");
                                	server = get_https_server(host, po);
                                }else{
                                	logger.info("Starting http Service");
                                	server = get_http_server(host, po);
                                }
                                if(server==null){
                                	System.exit(1);
                                }

                                ExecutorService rthreads = Executors.newFixedThreadPool(max_thread);
                               
                                server.setExecutor(rthreads);
                                server.start();

                                Endpoint ep_api = Endpoint.create(new API());
                                HttpContext context_api = server.createContext(api_path);
                                context_api.setAuthenticator(new IDASAuthentication("IDAS API Access"));
                                ep_api.publish(context_api);
                                
                                
                            } else {
                                logger.error("No API Port defined for the service to start on");
                            }

                        } catch (Exception e) {
                            logger.error("Problem while starting the API service");
                            logger.error(e);
                            e.printStackTrace();
                        }

                    }
                }.start();

            }else{
            	HttpsServer server = get_https_server("172.16.1.75", 8090);
            }
            
            new Thread(){
            	public void run(){
            		try {
	            		String sys_interface = System.getProperty("SERVICE_INTERFACE_FIFO", "/var/run/idas/service-interface");
		            	while(true){
		            		FileInputStream fio;
							
								fio = new FileInputStream(new File(sys_interface));
							
		        			BufferedReader reader = new BufferedReader(new InputStreamReader(fio));
		        			String sttr = reader.readLine().trim();
		        			logger.fatal("The system call "+sttr +" was send via "+sys_interface);
		        			
		        			String splilt[] = sttr.split(" ");
		        			if(splilt!=null && splilt.length == 3){
		        				if(splilt[0].equalsIgnoreCase("SET")){
		        					String prop = splilt[1].trim();
		        					String out = System.getProperty(prop.toUpperCase());
		        					if(out!=null){
		        						boolean is_num=false, is_bool=false, prop_change = false;
		        						try{
		        							Double v = Double.parseDouble(out);
		        							is_num = true;
		        						}catch (Exception e) {
											// TODO: handle exception
										}
		        						if(is_num==false){
		        							try {
		        								Boolean b = Boolean.parseBoolean(out);
		        								is_bool = true;
		        							} catch (Exception e) {
											// TODO: handle exception
		        							}
		        						}
		        						//Find the type of the value 
		        						String val = splilt[2].trim();
		        						if(is_num){		
		        							try{
			        							Double vsl = Double.parseDouble(val);
			        							prop_change = true;
			        						}catch (Exception e) {
												logger.fatal("Property change failed due type mismatch, property "+prop+" require a numaric format");
											}
		        						}else if (is_bool){
		        							try {
		        								Boolean b = Boolean.parseBoolean(val);
		        								prop_change = true;
		        							} catch (Exception e) {
		        								logger.fatal("Property change failed due type mismatch, property "+prop+" require a numaric format");
		        							}
		        						}else{
		        							// treat it as a string 
		        						}
		        						if(prop_change){
		        							logger.fatal("The system property "+prop +" was changed form "+out+" to "+val);
		        							System.setProperty(prop.toUpperCase(), val);
		        						}
		        					}else{
		        						logger.fatal("Invalid property "+prop);
		        					}
		        				}else{
		        					logger.fatal("Invalid system command, the command format should be SET [PROPERTY_NAME] [VALUE]");
		        				}
		        			}else{
		        				logger.fatal("Invalid system command, the command format should be SET [PROPERTY_NAME] [VALUE]");
		        			}
		        			
		        			reader.close(); 
		        			fio.close();
		            	}
            		} catch (Exception e) {
						logger.error("Could not connect to the service interface but the system will be started ");
						e.printStackTrace();
					} 
            	}
            	
            }.start();
            
            logger.info("IDAS Started @ " + new java.util.Date());
            Thread ct = Thread.currentThread();
            while (true) {
                synchronized (ct) {
                    TouchFile.touch(touchFile);
                    try {
                        ct.wait(60000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static HttpsServer get_https_server(String ip, int port) {
        HttpsServer server = null;
        try {
            InetSocketAddress inet = new InetSocketAddress(ip,port);
            // 2nd arg = max number of client requests to queue
            server = HttpsServer.create(inet, 5);

            SSLContext ssl_ctx = SSLContext.getInstance("TLS");
            // password for keystore
            char[] password = System.getProperty("KEYSTORE_PWD").toCharArray();
            KeyStore ks = KeyStore.getInstance("JKS");
            FileInputStream fis = new FileInputStream(System.getProperty("KEYSTORE"));
            ks.load(fis, password);

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, password);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ks);
            ssl_ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            // Making sure all the client services that I am communicating with have the 
            // same keystore and i do trust them
            HttpsURLConnection.setDefaultSSLSocketFactory(ssl_ctx.getSocketFactory());
            HttpsURLConnection.setDefaultAllowUserInteraction(false);
            // let people run the services in different host to the cn given in the certificate
    		HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
    			
    			public boolean verify(String arg0, SSLSession arg1) {
    				//skip any host name verifications
    				return true;
    			}
    		}); 
            // Create SSL engine and configure HTTPS to use it.
            final SSLEngine eng = ssl_ctx.createSSLEngine();
            server.setHttpsConfigurator(new HttpsConfigurator(ssl_ctx) {

                public void configure(HttpsParameters params) {
                    params.setCipherSuites(eng.getEnabledCipherSuites());
                    params.setProtocols(eng.getEnabledProtocols());
                }
            });

        } catch (Exception e) {
        	logger.error(e);
            e.printStackTrace();
            return null;
        }
        return server;
    }
    
    
    private static HttpServer get_http_server(String host,int port){
    	HttpServer server = null;
    	InetSocketAddress add = new InetSocketAddress(host,port);
    	try {
			server = HttpServer.create(add, 5);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error(e.getMessage());
			e.printStackTrace();
			return null;
		}
    	return server;
    }
}
